﻿using System;
using System.Collections.Generic;
using SFML.Window;
using Transformable_Engine_v2.Engine.Global;

namespace Transformable_Engine_v2.Engine.Input
{
    public static class GamepadHandler
    {
        public delegate void GamepadButtonInputHandler(uint joystickId, KeyboardInput.Key buttonId);

        public static event GamepadButtonInputHandler OnButtonPressed;
        public static event GamepadButtonInputHandler OnButtonReleased;

        public delegate void GamepadConnectionHandler(uint joystickId);

        public static event GamepadConnectionHandler OnConnect;
        public static event GamepadConnectionHandler OnDisconnect;

        public delegate void GamepadJoystickInputHandler(uint joystickId, KeyboardInput.Key joystick, float value);

        public static event GamepadJoystickInputHandler OnJoystickMovedAnalog;

        private const float Sensitivity = 60;

        private static readonly Dictionary<int, float> _inputState = new Dictionary<int, float>(); 

        public static void OnJoystickButtonPressed(object sender, JoystickButtonEventArgs e)
        {
            var button = (KeyboardInput.Key) ((int) KeyboardInput.Key.GamepadButton0 + e.Button);

            if(OnButtonPressed != null)
                Game.AddToEventQueue(() => OnButtonPressed(e.JoystickId, button));

            Log.LogInfo("[" + e.JoystickId + "] Button pressed: " + button);
        }

        public static void OnJoystickButtonReleased(object sender, JoystickButtonEventArgs e)
        {
            var button = (KeyboardInput.Key) ((int) KeyboardInput.Key.GamepadButton0 + e.Button);

            if (OnButtonReleased != null)
                Game.AddToEventQueue(() => OnButtonReleased(e.JoystickId, button));

            Log.LogInfo("[" + e.JoystickId + "] Button released: " + button);
        }

        public static void OnJoystickConnected(object sender, JoystickConnectEventArgs e)
        {
            if (OnConnect != null)
                Game.AddToEventQueue(() => OnConnect(e.JoystickId));

            Log.LogInfo("[" + e.JoystickId + "] Joystick connected");
        }

        public static void OnJoystickDisconnected(object sender, JoystickConnectEventArgs e)
        {
            if (OnDisconnect != null)
                Game.AddToEventQueue(() => OnDisconnect(e.JoystickId));

            Log.LogInfo("[" + e.JoystickId + "] Joystick disconnected");
        }

        public static void OnJoystickMoved(object sender, JoystickMoveEventArgs e)
        {

            KeyboardInput.Key input = (KeyboardInput.Key)((int)KeyboardInput.Key.JoystickX + (int)e.Axis);

            int key = ToDictionaryIndex(e.JoystickId, (int) e.Axis);
            if (_inputState.ContainsKey(key))
            {
                //Log.LogDebug(input + " moved from " + _inputState[key] + " to " + e.Position);

                if (e.Position > Sensitivity && _inputState[key] <= Sensitivity)
                {
                    if (OnButtonPressed != null)
                    {
                        Game.AddToEventQueue(() => OnButtonPressed(e.JoystickId, input));
                    }
                }
                else if (e.Position <= Sensitivity && _inputState[key] > Sensitivity)
                {  
                    if (OnButtonReleased != null)
                    {
                        Game.AddToEventQueue(() => OnButtonReleased(e.JoystickId, input));
                    }
                }

                if (e.Position > -Sensitivity && _inputState[key] <= -Sensitivity)
                {
                    if (OnButtonPressed != null)
                    {
                        Game.AddToEventQueue(() => OnButtonReleased(e.JoystickId, Opposite(input)));
                    }
                }
                else if (e.Position <= -Sensitivity && _inputState[key] > -Sensitivity)
                {
                    if (OnButtonReleased != null)
                    {
                        Game.AddToEventQueue(() => OnButtonPressed(e.JoystickId, Opposite(input)));
                    }
                }

                _inputState[key] = e.Position;

            }
            else
            {
                _inputState.Add(key, e.Position);
            }

            if (OnJoystickMovedAnalog != null)
                Game.AddToEventQueue(() => OnJoystickMovedAnalog(e.JoystickId, input, e.Position));
        }

        static KeyboardInput.Key Opposite(KeyboardInput.Key key)
        {
            switch (key)
            {
                case KeyboardInput.Key.JoystickX:
                    return KeyboardInput.Key.JoystickMX;
                case KeyboardInput.Key.JoystickY:
                    return KeyboardInput.Key.JoystickMY;
                case KeyboardInput.Key.JoystickZ:
                    return KeyboardInput.Key.JoystickMZ;
                case KeyboardInput.Key.JoystickR:
                    return KeyboardInput.Key.JoystickMR;
                case KeyboardInput.Key.JoystickU:
                    return KeyboardInput.Key.JoystickMU;
                case KeyboardInput.Key.JoystickV:
                    return KeyboardInput.Key.JoystickMV;
                case KeyboardInput.Key.JoystickPovX:
                    return KeyboardInput.Key.JoystickPovMX;
                case KeyboardInput.Key.JoystickPovY:
                    return KeyboardInput.Key.JoystickPovMY;
                default:
                    throw new ArgumentOutOfRangeException("key");
            }
        }

        static int ToDictionaryIndex(uint joystickId, int axis)
        {
            return (int) (joystickId*1000 + axis);
        }
    }
}